package br.unifor.cct.mia.gui.runner;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import br.unifor.cct.mia.coevolution.CoevolutionaryDomain;
import br.unifor.cct.mia.coevolution.InvalidTypeException;
import br.unifor.cct.mia.coevolution.evaluate.EvaluateUtils;
import br.unifor.cct.mia.coevolution.evaluate.util.RepeatableMemoryUtils;
import br.unifor.cct.mia.coevolution.memory.MemoryIndividual;
import br.unifor.cct.mia.coevolution.memory.RepeatableSharedMemory;
import br.unifor.cct.mia.coevolution.memory.SharedMemory;
import br.unifor.cct.mia.dataenhancement.Database;
import br.unifor.cct.mia.dataenhancement.GenotypeConverter;
import br.unifor.cct.mia.dataenhancement.Structure;
import br.unifor.cct.mia.dataenhancement.Tuple;
import br.unifor.cct.mia.evaluate.classification.WekaClassification;
import br.unifor.cct.mia.evolutionary.Individual;
import br.unifor.cct.mia.evolutionary.Specie;
import br.unifor.cct.mia.evolutionary.SpeciesConstants;
import br.unifor.cct.mia.ga.Ga;
import br.unifor.cct.mia.ga.GaFC;
import br.unifor.cct.mia.ga.coevolutionary.CoevGaFC;
import br.unifor.cct.mia.ga.coevolutionary.CoevGaFS;
import br.unifor.cct.mia.ga.coevolutionary.CoevGaIS;
import br.unifor.cct.mia.ga.coevolutionary.CoevGaMV;
import br.unifor.cct.mia.ga.coevolutionary.RepCoevGaFC;
import br.unifor.cct.mia.ga.coevolutionary.RepCoevGaFS;
import br.unifor.cct.mia.ga.coevolutionary.RepCoevGaIS;
import br.unifor.cct.mia.ga.coevolutionary.RepCoevGaMV;
import br.unifor.cct.mia.gui.AGMiningClassifierPanelAbstract;
import br.unifor.cct.mia.gui2.CoevolutionItem;
import br.unifor.cct.mia.gui2.CoevolutionItem.Especie;
import br.unifor.cct.mia.pso.coevolutionary.CoevPSOFS;
import br.unifor.cct.mia.pso.coevolutionary.CoevPSOIS;
import br.unifor.cct.mia.pso.coevolutionary.RepCoevPSOFS;
import br.unifor.cct.mia.pso.coevolutionary.RepCoevPSOIS;
import br.unifor.cct.mia.util.CSVWriter;
import br.unifor.cct.mia.util.LoadFile;
import br.unifor.cct.mia.util.Utils;
import br.unifor.cct.mia.util.serializer.Serializer;
import br.unifor.cct.mia.util.serializer.SerializerMemoryObject;

public class DomainRunnerGAPSO extends Thread {

	private static final String RESULT_TEST_TXT = "temp/resultTest.txt";
	private static final String RESULT_TXT = "temp/result.txt";
	private String csvFileName = "temp/executionResult.csv";
	private String serialFile = "";
	private DecimalFormat decFormat = new DecimalFormat("##.###");

	private AGMiningClassifierPanelAbstract frame;
	private Structure st;
	private Database db;
	private List positions;
	private Tuple tuples;
	private String[] options = null;
	private boolean repeatable;
	private int nExec = 1;
	private List<CoevolutionItem> especies;
	int learnerType = 0;
	int geracoes = 0;
	
	private int nBits=0;
	
	public DomainRunnerGAPSO( AGMiningClassifierPanelAbstract frame, Structure st, Database db, String[] options, 
			boolean repeatable, String serialFile, int nExec, String csvFileName, int geracoes,
			List<CoevolutionItem> especies, int learnerType) {
		this.frame=frame;
		this.st = st;
		this.db = db;
		this.options = options;
		this.repeatable = repeatable;
		this.serialFile = serialFile;
		this.nExec = nExec;
		this.csvFileName = csvFileName;
		this.learnerType = learnerType;
		this.especies = especies;
		this.geracoes = geracoes;
	}

	public void run() {		

		frame.log("Processando...");	

		CSVWriter writer = null;
		try {
			writer = new CSVWriter(csvFileName);
		} catch (IOException e) {
			e.printStackTrace();
			return;
		}

		try {
			for (int i = 0; i < nExec; i++) {

				Date d0 = new Date();
				CoevolutionaryDomain domain = null;
				if ( !repeatable ) {
					domain = createDomain();					
				}
				else {
					domain = createRepeatableDomain();
				}
				domain.start();	

				SharedMemory memory = domain.getBest();	

				double testeXValidation = teste(memory);

				Date d1 = new Date();
				int genBest = domain.getGenOfBest();
				frame.finalizarExecucao(memory.getFitness(),testeXValidation, genBest, String.valueOf( (d1.getTime()-d0.getTime())/1000 ),(i == nExec - 1) );

				String line = decFormat.format(memory.getFitness()) + ";"
				+ decFormat.format(testeXValidation) + ";"
				+ 0 + ";"
				+ String.valueOf((d1.getTime() - d0.getTime()) / 1000);

				writer.addLine(line);
				if ((i == nExec - 1))
					writer.close();

				if ( !serialFile.equals("") ) {
					try {
						Serializer serializer = new Serializer();
						SerializerMemoryObject obj = new SerializerMemoryObject();
						obj.setMemory(memory);
						obj.setDbFile(db.getDbFile());
						obj.setStrucFile(st.getStrucFile());
						obj.setLearnerType(learnerType);
						obj.setOptions(options);
						obj.setRepeatable(repeatable);
						serializer.serializer(obj,serialFile);
					} catch (FileNotFoundException e) {
						e.printStackTrace();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
				Utils.clearTemp();
			}		
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
		finally {
			frame.log("Processamento Finalizado");			
		}		  
	}

	private double teste(SharedMemory memory) throws IOException, 
	InterruptedException, 
	FileNotFoundException, 
	InvalidTypeException {
		double testeXValidation = 0;

		File trainFile = null;
		File testFile = null;
		if ( repeatable ) {
			CoevolutionItem item = especies.get(0);
			int specieType = item.getSpecieType();
			
			ArrayList list = memory.getElements();
			MemoryIndividual element = (MemoryIndividual) list.get(0);
			Individual ind = element.getIndividual();

			RepeatableSharedMemory memoryAux = new RepeatableSharedMemory();
			MemoryIndividual mInd = new MemoryIndividual(ind,specieType, nBits);
			memoryAux.add(mInd);

			//Inicia o individuo com o primeiro
			Individual individual = RepeatableMemoryUtils.createFirstIndividual(ind, specieType, st, db, positions, nBits);
			//Busca os outros para montar o indiviudo completo
			RepeatableMemoryUtils.addMemoryToIndividual(ind, specieType, memoryAux, (RepeatableSharedMemory)memory, individual);

			trainFile = RepeatableMemoryUtils.createResultFile(specieType, st, db, tuples, positions, RESULT_TXT, individual, nBits);

			// ============ DADOS PARA TESTE =============
			Database dbTeste = LoadFile.loadDatabase(frame.getTestFilePath() );

			if ( specieType != SpeciesConstants.INSTANCE_SELECTION &&
					specieType != SpeciesConstants.MISSING_VALUE ) {
				memoryAux = new RepeatableSharedMemory();
				mInd = new MemoryIndividual(ind,specieType, nBits);
				memoryAux.add(mInd);

				//Inicia o individuo com o primeiro
				individual = RepeatableMemoryUtils.createFirstIndividual(ind, specieType, st, dbTeste, positions, nBits);
				//Busca os outros para montar o indiviudo completo
				RepeatableMemoryUtils.addMemoryToIndividual(ind, specieType, memoryAux, (RepeatableSharedMemory)memory, individual);

				testFile = RepeatableMemoryUtils.createResultFile(specieType, st, dbTeste, tuples, positions, RESULT_TEST_TXT, individual, nBits);
			}
			else {
				GenotypeConverter converter = new GenotypeConverter();
				testFile = converter.fullConvert(RESULT_TEST_TXT, st,dbTeste);
			}
		}
		else {
			trainFile = EvaluateUtils.memoryToFile(memory,RESULT_TXT,st,db,tuples,positions,nBits);
			Database dbTeste = LoadFile.loadDatabase(frame.getTestFilePath() );
			testFile = EvaluateUtils.memoryToFile(memory,RESULT_TEST_TXT,st,dbTeste,tuples,positions,nBits);
		}

		WekaClassification classifier = new WekaClassification(learnerType,options);
		//classifier.setTestType(ClassifierPanel.SUPPLIED_TEST_SET);
		testeXValidation = classifier.evaluate(trainFile,testFile);
		return testeXValidation;
	}	

	//**************************
	private CoevolutionaryDomain createDomain() throws InvalidTypeException {

		CoevolutionaryDomain domain = new CoevolutionaryDomain(CoevolutionaryDomain.MEMORY,learnerType);
		ArrayList species =  new ArrayList();

		List positions = null;
		Tuple tuples = null;

		for ( int i=0; i<especies.size(); i++ ) {
			CoevolutionItem item = especies.get(i);

			if ( item.getSpecieType()==0 )/*Feat. Selection*/ {
				Specie coev = null;
				if ( item.getEspecie().getSpecie() == Especie.GA.getSpecie() ) {
					coev = new CoevGaFS(db,st,domain,options);
					((CoevGaFS)coev).setLearnerType(learnerType);
					((CoevGaFS)coev).setMAXGENS( geracoes );
					((CoevGaFS)coev).setPOPSIZE( item.getPopulacao() );
					((CoevGaFS)coev).setPMUTATION( item.getMutacao() );
					((CoevGaFS)coev).setPXOVER( item.getCrossover() );
				}
				else {
					coev = new CoevPSOFS(db,st,domain,options);
					((CoevPSOFS)coev).setLearnerType(learnerType);
					((CoevPSOFS)coev).setExecSize(geracoes);
					((CoevPSOFS)coev).setPopSize(item.getPopulacao());
				}	                    
				species.add( coev );
			}
			else if ( item.getSpecieType()==1 )/*Feat. Const.*/ {
				Specie coev = null;
				if ( item.getEspecie().getSpecie() == Especie.GA.getSpecie() ) {
					coev = new CoevGaFC(db,st,domain,options);
					((CoevGaFC)coev).setLearnerType(learnerType);
					((CoevGaFC)coev).setMAXGENS( geracoes );
					((CoevGaFC)coev).setPOPSIZE( item.getPopulacao() );
					((CoevGaFC)coev).setPMUTATION( item.getMutacao() );
					((CoevGaFC)coev).setPXOVER( item.getCrossover() );
				}
				else {
					//TODO
				}						                    
				species.add( coev );		                	
			}
			else if ( item.getSpecieType()==2 ) /*Instance Selection*/ {
				Specie coev = null;
				if ( item.getEspecie().getSpecie() == Especie.GA.getSpecie() ) {
					coev = new CoevGaIS(db,st,domain,options);
					((CoevGaIS)coev).setLearnerType(learnerType);
					((CoevGaIS)coev).setMAXGENS( geracoes );
					((CoevGaIS)coev).setPOPSIZE( item.getPopulacao() );
					((CoevGaIS)coev).setPMUTATION( item.getMutacao() );
					((CoevGaIS)coev).setPXOVER( item.getCrossover() );
				}
				else {
					coev = new CoevPSOIS(db,st,domain,options);
					((CoevPSOIS)coev).setLearnerType(learnerType);
					((CoevPSOIS)coev).setExecSize(geracoes);
					((CoevPSOIS)coev).setPopSize(item.getPopulacao());
				}	                    
				species.add( coev );				
			}
			else if ( item.getSpecieType()==3 ) {	
				positions = LoadFile.getFaultDatabase(st,db);
				tuples = new Tuple();
				db.mountStr(tuples, positions);                	
				domain.setTuples(tuples);
				domain.setPositions(positions);

				Ga gaCoev = new CoevGaMV(st,db,domain,positions,positions.size(),tuples,options);
				gaCoev.setLearnerType(learnerType);
				gaCoev.setMAXGENS( geracoes );
				gaCoev.setPOPSIZE( item.getPopulacao() );
				gaCoev.setPMUTATION( item.getMutacao() );
				gaCoev.setPXOVER( item.getCrossover() );		                	
				species.add(gaCoev );
			}		                
		}

		domain.setSpecies(species);
		domain.setOptions(options);

		this.positions = positions;
		this.tuples = tuples;
		return domain;
	}

	private CoevolutionaryDomain createRepeatableDomain() throws InvalidTypeException {
		CoevolutionaryDomain domain = new CoevolutionaryDomain(CoevolutionaryDomain.REPEATABLE_MEMORY,learnerType);
		ArrayList species =  new ArrayList();

		boolean first = true;
		List positions = null;
		Tuple tuples = null;

		for ( int i=0; i<especies.size(); i++ ) {
			CoevolutionItem item = especies.get(i);

			if ( item.getSpecieType()==0 )/*Feat Sel*/ {
				item.setSpecieType(SpeciesConstants.FEATURE_SELECTION);
				int attSize = st.size()-1;
				int indivSize = attSize/especies.size();

				Specie coev = null;
				if ( item.getEspecie().getSpecie() == Especie.GA.getSpecie() ) {
					coev = new RepCoevGaFS(db,st,domain,indivSize,i,options);
					((RepCoevGaFS)coev).setLearnerType(learnerType);
					((RepCoevGaFS)coev).setMAXGENS( geracoes );
					((RepCoevGaFS)coev).setPOPSIZE( item.getPopulacao() );
					((RepCoevGaFS)coev).setPMUTATION( item.getMutacao() );
					((RepCoevGaFS)coev).setPXOVER( item.getCrossover() );
				}
				else {
					coev = new RepCoevPSOFS(db,st,domain,indivSize,i,options);
					((RepCoevPSOFS)coev).setLearnerType(learnerType);
					((RepCoevPSOFS)coev).setExecSize(geracoes);
					((RepCoevPSOFS)coev).setPopSize(item.getPopulacao());
				}	
					                    
				species.add( coev );
			}	
			else if (item.getSpecieType()==1)/*Feat Const*/ {
				item.setSpecieType(SpeciesConstants.FEATURE_CONSTRUCTION);
				Specie coev = null;
				if ( item.getEspecie().getSpecie() == Especie.GA.getSpecie() ) {
					int indivSize = GaFC.GENOTYPE_SIZE/especies.size();
					
					coev = new RepCoevGaFC(db,st,domain,indivSize,i,options);
					((RepCoevGaFC)coev).setLearnerType(learnerType);
					((RepCoevGaFC)coev).setMAXGENS( geracoes );
					((RepCoevGaFC)coev).setPOPSIZE( item.getPopulacao() );
					((RepCoevGaFC)coev).setPMUTATION( item.getMutacao() );
					((RepCoevGaFC)coev).setPXOVER( item.getCrossover() );
				}
				else {
					//TODO
				}				                    
				species.add( coev );
			}
			else if ( item.getSpecieType()==2 )/*Inst Select*/ {
				item.setSpecieType(SpeciesConstants.INSTANCE_SELECTION);
				int indivSize = db.size()/especies.size();

				Specie coev = null;
				if ( item.getEspecie().getSpecie() == Especie.GA.getSpecie() ) {
					coev = new RepCoevGaIS(db,st,domain,indivSize,i,options);
					((RepCoevGaIS)coev).setLearnerType(learnerType);
					((RepCoevGaIS)coev).setMAXGENS( geracoes );
					((RepCoevGaIS)coev).setPOPSIZE( item.getPopulacao() );
					((RepCoevGaIS)coev).setPMUTATION( item.getMutacao() );
					((RepCoevGaIS)coev).setPXOVER( item.getCrossover() );
				}
				else {
					coev = new RepCoevPSOIS(db,st,domain,indivSize,i,options);
					((RepCoevPSOIS)coev).setLearnerType(learnerType);
					((RepCoevPSOIS)coev).setExecSize(geracoes);
					((RepCoevPSOIS)coev).setPopSize(item.getPopulacao());
				}				                    
				species.add( coev );
			}
			else if ( item.getSpecieType()==3 ) /*Missing Value*/ {
				item.setSpecieType(SpeciesConstants.MISSING_VALUE);
				if ( first ) {
					positions = LoadFile.getFaultDatabase(st,db);
					tuples = new Tuple();
					db.mountStr(tuples, positions);                	
					domain.setTuples(tuples);
					domain.setPositions(positions);
					first=false;

					this.positions = positions;
					this.tuples = tuples;
				}
				int indivSize = positions.size()/especies.size();

				Ga gaCoev = new RepCoevGaMV(st,db,domain,positions,indivSize,tuples,i,options);
				gaCoev.setLearnerType(learnerType);
				gaCoev.setMAXGENS( geracoes );
				gaCoev.setPOPSIZE( item.getPopulacao() );
				gaCoev.setPMUTATION( item.getMutacao() );
				gaCoev.setPXOVER( item.getCrossover() );	                    
				species.add( gaCoev );
			}		  
		}		            
		domain.setSpecies(species);
		domain.setOptions(options);		

		return domain;
	}	
}
